package models

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"time"
)

const (
	TimeFormat = "2006-01-02 15:04:05"
)

// swagger:model
type BaseModel struct {
	ID      uint64 `json:"id" gorm:"primary_key;AUTO_INCREMENT;column:id"`
	Crtime  Time   `json:"crtime"	gorm:"column:crtime"`
	Modtime Time   `json:"modtime"	gorm:"column:modtime"`
}

func (baseModel *BaseModel) BeforeCreate(tx *gorm.DB) (err error) {
	crtime := Time{time.Now()}
	baseModel.Crtime = crtime
	return
}

func (baseModel *BaseModel) BeforeUpdate(tx *gorm.DB) (err error) {
	crtime := Time{time.Now()}
	baseModel.Modtime = crtime
	return
}

type Time struct {
	time.Time
}

func (t *Time) UnmarshalJSON(data []byte) (err error) {
	now, err := time.ParseInLocation(`"`+TimeFormat+`"`, string(data), time.Local)
	*t = Time{Time: now}
	return
}
func (t Time) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(TimeFormat)+2)
	b = append(b, '"')
	b = t.Time.AppendFormat(b, TimeFormat)
	b = append(b, '"')
	return b, nil
}
func (t Time) String() string {
	return t.Time.Format(TimeFormat)
}

func (t Time) Value() (driver.Value, error) {
	var zeroTime time.Time
	if t.Time.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return t.Time.Format(TimeFormat), nil
}

// Scan valueof time.Time
func (t *Time) Scan(v interface{}) error {
	value, ok := v.(time.Time)
	if ok {
		*t = Time{Time: value}
		return nil
	}
	return fmt.Errorf("can not convert %v to timestamp", v)
}

type JSON json.RawMessage

// 实现 sql.Scanner 接口，Scan 将 value 扫描至 Jsonb
func (j *JSON) Scan(value interface{}) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}

	result := json.RawMessage{}
	err := json.Unmarshal(bytes, &result)
	*j = JSON(result)
	return err
}

// 实现 driver.Valuer 接口，Value 返回 json value
func (j JSON) Value() (driver.Value, error) {
	if len(j) == 0 {
		return nil, nil
	}
	return json.RawMessage(j).MarshalJSON()
}

// MarshalJSON to output non base64 encoded []byte
func (j JSON) MarshalJSON() ([]byte, error) {
	return json.RawMessage(j).MarshalJSON()
}

// UnmarshalJSON to deserialize []byte
func (j *JSON) UnmarshalJSON(b []byte) error {
	result := json.RawMessage{}
	err := result.UnmarshalJSON(b)
	*j = JSON(result)
	return err
}

func (j JSON) String() string {
	return string(j)
}
